﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Compiler
{
    public class ParseTable
    {
        private Dictionary<Entry, ParseTableAction> actionTable;
        private Dictionary<Entry, SimpleParseTableState> jumpTable;
        public SimpleParseTableState Start;

        public ParseTable()
        {
            actionTable = new Dictionary<Entry, ParseTableAction>(new EntryEqualityComparer());
            jumpTable = new Dictionary<Entry, SimpleParseTableState>(new EntryEqualityComparer());
        }
       
        public bool TryGetAction(SimpleParseTableState state, Symbol input, out ParseTableAction action)
        {
            return actionTable.TryGetValue(new Entry(state, input), out action);
        }
        
        public bool GetJumptableState(SimpleParseTableState state, Symbol symbol, out SimpleParseTableState followState)
        {
            return jumpTable.TryGetValue(new Entry(state, symbol), out followState);
        }

        public void AddJumpRule(SimpleParseTableState state, Symbol symbol, SimpleParseTableState newstate)
        {
            jumpTable.Add(new Entry(state, symbol), newstate);
        }

        private void AddActionRule(SimpleParseTableState state, Symbol symbol, ParseTableAction action)
        {
            actionTable.Add(new Entry(state, symbol), action );
        }

        public void AddAccept(SimpleParseTableState state, Symbol symbol)
        {
            AddActionRule(state, symbol, new Accept());
        }

        public void AddShift(SimpleParseTableState state, Symbol symbol, SimpleParseTableState newstate)
        {
            AddActionRule(state, symbol, new Shift(newstate));
        }

        public void AddReduce(SimpleParseTableState state, Symbol symbol, Production production)
        {
            AddActionRule(state, symbol, new Reduce(production, this));
        }

        public string Print()
        {
            var symCmp = new Symbol.SymbolEqualityComparer();
            var stateCmp = new SimpleParseTableState.SimpleParseTableStateEqualityComparer();
            List<Symbol> term = actionTable.Keys.Select( k => k.symbol).Distinct(symCmp).ToList();
            List<Symbol> nonterm = jumpTable.Keys.Select(k => k.symbol).Distinct(symCmp).ToList();
            List<SimpleParseTableState> state = actionTable.Keys.Select(k => k.state).Distinct(stateCmp).OrderBy( s => s.index).ToList();

            StringBuilder b = new StringBuilder();
            b.Append("\n");
            b.Append("\t");
            foreach (var t in term)
            {
                b.Append(t.ToString() + "\t");
            }
            foreach (var t in nonterm)
            {
                b.Append(t.ToString() + "\t");
            }
            b.Append("\n\n");

            foreach (var s in state)
            {
                b.Append(s.index+"\t");
                foreach (var t in term)
                {
                    var entry = new Entry(s,t);
                    if (actionTable.ContainsKey(entry))
                        b.Append(actionTable[entry].ToString() + "\t");
                    else
                        b.Append("  \t");
                }

                foreach (var t in nonterm)
                {
                    var entry = new Entry(s, t);
                    if (jumpTable.ContainsKey(entry))
                        b.Append(jumpTable[entry].ToString() + "\t");
                    else
                        b.Append("  \t");
                }

                b.Append("\n\n");

            }

            return b.ToString();
        }
    }
}
